package be.rivendale.mathematics.intersection;

import be.rivendale.mathematics.*;
import org.junit.Assert;
import org.junit.Test;

import static be.rivendale.mathematics.MathematicalAssert.assertPointEquals;
import static be.rivendale.mathematics.intersection.RayIntersectionMode.afterPassThroughPoint;
import static be.rivendale.mathematics.intersection.RayIntersectionMode.betweenOriginAndPassThroughPoint;
import static org.junit.Assert.*;

public class IntersectionTest {
    @Test(expected = IllegalArgumentException.class)
    public void constructorForTriangleThrowsIllegalArgumentExceptionWhenRayIsNull() {
        Triangle triangle = new Triangle(new Vertex(1, 2, 3), new Vertex(-4, 1, 3), new Vertex(3, -3, 3));
		new Intersection(null, triangle, afterPassThroughPoint);
    }

	@Test(expected = IllegalArgumentException.class)
	public void constructorForTriangleThrowsIllegalArgumentExceptionWhenIntersectionModeIsNull() {
		Triangle triangle = new Triangle(new Vertex(1, 2, 3), new Vertex(-4, 1, 3), new Vertex(3, -3, 3));
		Ray ray = new Ray(new Vertex(0, 1, 2), new Vertex(5, 8, 10));
		new Intersection(ray, triangle, null);
	}

	@Test(expected = IllegalArgumentException.class)
	public void constructorForTriangleThrowsIllegalArgumentExceptionWhenTriangleIsNull() {
		Ray ray = new Ray(new Vertex(0, 1, 2), new Vertex(5, 8, 10));
		new Intersection(ray, (Triangle)null, afterPassThroughPoint);
	}

	@Test(expected = IllegalArgumentException.class)
    public void constructorForRectangleThrowsIllegalArgumentExceptionWhenRayIsNull() {
        Rectangle rectangle = new Rectangle(new Vertex(1, 2, 3), new Vertex(-4, 1, 3), new Vertex(3, -3, 3));
		new Intersection(null, rectangle, afterPassThroughPoint);
    }

	@Test(expected = IllegalArgumentException.class)
	public void constructorForRectangleThrowsIllegalArgumentExceptionWhenIntersectionModeIsNull() {
		Rectangle rectangle = new Rectangle(new Vertex(1, 2, 3), new Vertex(-4, 1, 3), new Vertex(3, -3, 3));
		Ray ray = new Ray(new Vertex(0, 1, 2), new Vertex(5, 8, 10));
		new Intersection(ray, rectangle, null);
	}

	@Test(expected = IllegalArgumentException.class)
	public void constructorForRectangleThrowsIllegalArgumentExceptionWhenTriangleIsNull() {
		Ray ray = new Ray(new Vertex(0, 1, 2), new Vertex(5, 8, 10));
		new Intersection(ray, (Rectangle)null, afterPassThroughPoint);
	}

    @Test
    public void getIntersectionPointReturnsNullIfTheRayIsParallelToTheTriangle() throws Exception {
        Triangle triangle = new Triangle(new Vertex(1, 2, 3), new Vertex(-4, 1, 3), new Vertex(3, -3, 3));
        Ray ray = new Ray(new Triple(-2, -4, 1), new Triple(-2, 4, 1));
        assertNull(triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }

    @Test
    public void getIntersectionPointReturnsNullIfTheRayIsNotParallelToTheTriangleButGoesNextToIt() throws Exception {
        Triangle triangle = new Triangle(new Vertex(1, 2, 3), new Vertex(-4, 1, 2), new Vertex(3, -3, -2));
        Ray ray = new Ray(new Triple(-2, -4, 1), new Triple(-2, 4, -3));
        assertNull(triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }

    /**
     * Although there is an intersection in this case, we would not be able to "see" it, since the thickness of a triangle is infinitely thin.
     */
    @Test
    public void getIntersectionPointReturnsNullIfTheRayIntersectsWithTheTriangleBetweenTheTwoRayPointsAndBothRayAndTriangleAreOnTheSamePlane() {
        Triangle triangle = new Triangle(new Vertex(1.12, -5.17,  0.39), new Vertex(4.91, -2.03, 2.5), new Vertex(-4, 3, 2));
        Ray ray = new Ray(new Triple(-2, -4, 0), new Triple(4, 0, 3));
        assertNull(triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }

    /**
     * Although there is an intersection in this case, we would not be able to "see" it, since the thickness of a triangle is infinitely thin.
     */
    @Test
    public void getIntersectionPointReturnsNullIfTheRayIntersectsWithTheTriangleOutsideTheTwoRayPointsAndBothRayAndTriangleAreOnTheSamePlane() {
        Ray ray = new Ray(new Triple(-4, -3, 2), new Triple(-3, -2, 2));
        Triangle triangle = new Triangle(new Vertex(-3, 2, 2), new Vertex(4, 3, 2), new Vertex(2, -2, 2));
        assertNull(triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }

    @Test
    public void getIntersectionPointReturnsNullIfTheRayDoesNotIntersectWithTheTriangleAndBothAreOnTheSamePlane() {
        Triangle triangle = new Triangle(new Vertex(-2.44, -0.04, 1.31), new Vertex(1.89, 0.95, 2.79), new Vertex(-4, 3, 2));
        Ray ray = new Ray(new Triple(-2, -4, 0), new Triple(4, 0, 3));
        assertNull(triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }

    @Test
    public void getIntersectionPointReturnsNullIfTheRayIsNotParallelToTheTriangleButTheIntersectionIsOnTheOppositeSideOfTheOrigin() throws Exception {
        Triangle triangle = new Triangle(new Vertex(2, -3, 3), new Vertex(-4, -3, -1), new Vertex(5, 0, 2));
        Ray ray = new Ray(new Triple(2, -2, 1), new Triple(2, -3, 0));
        assertNull(triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }

    @Test
    public void getIntersectionPointReturnsNullIfTheRayIntersectsWithTheTriangleBetweenTheTwoRayPoints() {
        Triangle triangle = new Triangle(new Vertex(2, -3, 3), new Vertex(-4, -3, -1), new Vertex(5, 0, 2));
        Ray ray = new Ray(new Triple(-3, -2, 1), new Triple(2, -3, 0));
        assertNull(triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }

    @Test
    public void getIntersectionPointReturnsIntersectionPointWhenIntersectionExists() throws Exception {
        Triangle triangle = new Triangle(new Vertex(-3.5, -2.5, -1), new Vertex(0.75, -3.5, -2), new Vertex(3, 0, 5));
        Ray ray = new Ray(new Vertex(-6, 12, 5), new Vertex(-3.5, 5.5, 3));
        assertPointEquals(new Triple(-0.659091, -1.886364, 0.727273), triangle.intersection(ray, afterPassThroughPoint).getIntersectionPoint());
    }
    
    @Test
    public void doesIntersectReturnsTrueIfTheIntersectionOccursBetweenTheOriginAndThePassThroughPointAndTheIntersectionModeIsBetween() throws Exception {
        Triangle triangle = new Triangle(new Triple(0, 0, 0), new Triple(1, 2, 3), new Triple(3, 2, 1));
        Ray ray = new Ray(new Triple(-1, -4, 2), new Triple(2, 3, 1));
        Intersection intersection = triangle.intersection(ray, betweenOriginAndPassThroughPoint);
        Assert.assertTrue(intersection.doesIntersect());
    }

    @Test
    public void doesIntersectReturnsFalseIfTheIntersectionOccursBetweenTheOriginAndThePassThroughPointAndTheIntersectionModeIsAfter() throws Exception {
        Triangle triangle = new Triangle(new Triple(0, 0, 0), new Triple(1, 2, 3), new Triple(3, 2, 1));
        Ray ray = new Ray(new Triple(-1, -4, 2), new Triple(2, 3, 1));
        Intersection intersection = triangle.intersection(ray, afterPassThroughPoint);
        Assert.assertFalse(intersection.doesIntersect());
    }

    @Test
    public void doesIntersectReturnsTrueIfAnIntersectionExistsForATriangle() throws Exception {
        Triangle triangle = new Triangle(new Vertex(0.75, -3.5, 1.5), new Vertex(-0.25, 0.75, 1), new Vertex(1.25, 1.75, -1));
        Ray ray = new Ray(new Vertex(3, 3.75, 5), new Vertex(1, 0.75, 1));
        assertTrue(triangle.intersection(ray, afterPassThroughPoint).doesIntersect());
    }

    @Test
    public void doesIntersectReturnsFalseIfNoIntersectionExistsForATriangle() throws Exception {
        Triangle triangle = new Triangle(new Vertex(0.75, -3.5, 1.5), new Vertex(-0.25, 0.75, 1), new Vertex(1.25, 1.75, -1));
        Ray ray = new Ray(new Vertex(0, 0, 0), new Vertex(-1, -1, -1));
        assertFalse(triangle.intersection(ray, afterPassThroughPoint).doesIntersect());
    }
    
    @Test
    public void doesIntersectReturnsTrueIfAnIntersectionExistsForARectangle() throws Exception {
		Rectangle rectangle = new Rectangle(
			new Vertex(-1, -1, 0),
			new Vertex(1, -1, 0),
			new Vertex(-1, 1, 0)
		);
		Ray ray = new Ray(
			new Vertex(0.9, 0.9, 1),
			new Vertex(0.9, 0.9, -1)
		);
		Intersection intersection = new Intersection(ray, rectangle, betweenOriginAndPassThroughPoint);
		assertTrue(intersection.doesIntersect());
	}

	@Test
    public void doesIntersectReturnsFalseIfAnIntersectionDoesNotExistForARectangle() throws Exception {
		Rectangle rectangle = new Rectangle(
			new Vertex(-1, -1, 0),
			new Vertex(1, -1, 0),
			new Vertex(-1, 1, 0)
		);
		Ray ray = new Ray(
			new Vertex(1.1, 1.1, 1),
			new Vertex(1.1, 1.1, -1)
		);
		Intersection intersection = new Intersection(ray, rectangle, betweenOriginAndPassThroughPoint);
		assertFalse(intersection.doesIntersect());
    }
}
